package cn.lisens.valid.utils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;

import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;

/**
 * 手工校验工具类（模拟@Valid功能）
 */
public class ValidatorUtils {
    
	private static final Validator validator;

    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    private ValidatorUtils() {
        // 私有构造方法，防止实例化
    }
    
    /**
     * 手动校验对象
     *
     * @param object 需要校验的对象
     * @param groups 校验分组
     * @throws IllegalArgumentException 当校验失败时抛出异常，包含错误信息
     */
    public static void validate(Object object, Class<?>... groups) {
        Set<ConstraintViolation<Object>> violations = validator.validate(object, groups);
        if (!violations.isEmpty()) {
            Map<String, String> errorMap = new HashMap<>(16);
            for (ConstraintViolation<Object> violation : violations) {
                // 获取校验失败的字段名和错误信息
                String field = violation.getPropertyPath().toString();
                String message = violation.getMessage();
                errorMap.put(field, message);
            }
            throw new IllegalArgumentException(buildErrorMessage(errorMap));
        }
    }
    
    public static String validateGetError(Object object, Class<?>... groups) {
        try {
        	validate(object, groups);
        	return "";
		} catch (IllegalArgumentException e) {
			return e.getMessage();
		}
    }
    
    /**
     * 处理BindingResult中的校验错误
     *
     * @param bindingResult 绑定结果对象
     * @return 错误信息Map
     */
    public static Map<String, String> handleBindingResult(BindingResult bindingResult) {
        if (!bindingResult.hasErrors()) {
            return Collections.emptyMap();
        }

        Map<String, String> errorMap = new HashMap<>(16);
        // 处理字段错误
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        }

        // 处理全局错误
        List<ObjectError> globalErrors = bindingResult.getGlobalErrors();
        for (ObjectError globalError : globalErrors) {
            errorMap.put(globalError.getObjectName(), globalError.getDefaultMessage());
        }

        return errorMap;
    }
    
    /**
     * 从MethodArgumentNotValidException中提取错误信息
     *
     * @param e 方法参数无效异常
     * @return 错误信息Map
     */
    public static Map<String, String> extractErrors(MethodArgumentNotValidException e) {
        return e.getBindingResult().getFieldErrors().stream()
                .collect(Collectors.toMap(
                        FieldError::getField,
                        FieldError::getDefaultMessage,
                        (existing, replacement) -> existing + "；" + replacement
                ));
    }

    /**
     * 构建错误信息字符串
     *
     * @param errorMap 错误信息Map
     * @return 错误信息字符串
     */
    private static String buildErrorMessage(Map<String, String> errorMap) {
        StringBuilder sb = new StringBuilder("数据校验失败：");
        errorMap.forEach((field, message) -> sb.append(field).append("：").append(message).append("；"));
        // 移除最后一个分号
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 1);
        }
        return sb.toString();
    }
    
    /**
     * 校验对象（类似@Valid功能）
     * @param obj 要校验的对象
     * @param <T> 对象类型
     * @throws ValidationException 如果校验失败
     */
    public static <T> void validate(T obj) {
        if (obj == null) {
            throw new ValidationException("校验对象不能为null");
        }
        
        Set<ConstraintViolation<T>> violations = validator.validate(obj);
        if (!violations.isEmpty()) {
            String errorMsg = violations.stream()
                .map(v -> String.format("%s %s (实际值: %s)", 
                    v.getPropertyPath(), 
                    v.getMessage(), 
                    v.getInvalidValue()))
                .collect(Collectors.joining("; "));
            throw new ValidationException(errorMsg);
        }
        
        // 递归校验嵌套对象
        validateNestedObjects(obj);
    }
    
    /**
     * 递归校验对象中的嵌套对象
     * @param obj 要校验的对象
     */
    private static void validateNestedObjects(Object obj) {
        if (obj == null) {
            return;
        }
        
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            
            try {
                Object fieldValue = field.get(obj);
                if (fieldValue == null) {
                    continue;
                }
                
                // 校验集合/数组中的元素
                if (fieldValue instanceof Collection) {
                    for (Object item : (Collection<?>) fieldValue) {
                        validate(item);
                    }
                } else if (fieldValue.getClass().isArray()) {
                    for (Object item : (Object[]) fieldValue) {
                        validate(item);
                    }
                } 
                // 校验普通对象字段
                else if (!field.getType().isPrimitive() 
                        && !field.getType().getName().startsWith("java.")) {
                    validate(fieldValue);
                }
            } catch (IllegalAccessException e) {
                throw new ValidationException("校验嵌套对象时出错: " + e.getMessage());
            }
        }
    }
    
    /**
     * 校验对象并返回校验结果（不抛出异常）
     * @param obj 要校验的对象
     * @param <T> 对象类型
     * @return 校验结果
     */
    public static <T> ValidationResult validateWithResult(T obj) {
        try {
            validate(obj);
            return new ValidationResult(true, Collections.emptyList());
        } catch (ValidationException e) {
            return new ValidationResult(false, Arrays.asList(e.getMessage().split("; ")));
        }
    }
    
    /**
     * 校验结果封装类
     */
    public static class ValidationResult {
        private final boolean valid;
        private final List<String> errors;
        
        public ValidationResult(boolean valid, List<String> errors) {
            this.valid = valid;
            this.errors = errors;
        }
        
        public boolean isValid() {
            return valid;
        }
        
        public List<String> getErrors() {
            return errors;
        }
        
        public String getErrorMsg() {
            return String.join("; ", errors);
        }
    }
}